//   Copyright 2012,2013 Vaughn Vernon
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package com.saasovation.agilepm.domain.model.product.backlogitem;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import com.saasovation.agilepm.domain.model.Entity;
import com.saasovation.agilepm.domain.model.discussion.DiscussionAvailability;
import com.saasovation.agilepm.domain.model.discussion.DiscussionDescriptor;
import com.saasovation.agilepm.domain.model.product.ProductId;
import com.saasovation.agilepm.domain.model.product.release.Release;
import com.saasovation.agilepm.domain.model.product.release.ReleaseId;
import com.saasovation.agilepm.domain.model.product.sprint.Sprint;
import com.saasovation.agilepm.domain.model.product.sprint.SprintId;
import com.saasovation.agilepm.domain.model.team.TeamMember;
import com.saasovation.agilepm.domain.model.tenant.TenantId;
import com.saasovation.common.domain.model.DomainEventPublisher;

/**
 * @实体：待办事项
 */
public class BacklogItem extends Entity {

    /**
     * 相关问题编号
     */
    private String associatedIssueId;
    /**
     * 待办事项ID
     */
    private BacklogItemId backlogItemId;
    /**
     * 业务优先级
     */
    private BusinessPriority businessPriority;
    /**
     * 范围
     */
    private String category;
    /**
     * 待办事项讨论
     */
    private BacklogItemDiscussion discussion;
    /**
     * 讨论发起ID
     */
    private String discussionInitiationId;
    /**
     * 产品ID
     */
    private ProductId productId;
    /**
     * 发行ID
     */
    private ReleaseId releaseId;
    /**
     * 冲刺ID
     */
    private SprintId sprintId;
    /**
     * 待办事项状态
     */
    private BacklogItemStatus status;
    /**
     * 故事
     */
    private String story;
    /**
     * 故事点清单
     */
    private StoryPoints storyPoints;
    /**
     * 摘要
     */
    private String summary;
    /**
     * 任务清单
     */
    private Set<Task> tasks;
    /**
     * 租户ID
     */
    private TenantId tenantId;
    /**
     * 待办清单类型
     */
    private BacklogItemType type;

    /**
     * 由以下参数构造待办事项
     * @param aTenantId         租户ID
     * @param aProductId        产品ID
     * @param aBacklogItemId    待办事项ID
     * @param aSummary          摘要
     * @param aCategory         范围
     * @param aType             待办事项类型
     * @param aStatus           待办事项状态
     * @param aStoryPoints      故事点清单
     */
    public BacklogItem(
            TenantId aTenantId,
            ProductId aProductId,
            BacklogItemId aBacklogItemId,
            String aSummary,
            String aCategory,
            BacklogItemType aType,
            BacklogItemStatus aStatus,
            StoryPoints aStoryPoints) {
        // 调用默认构造器
        this();

        this.setBacklogItemId(aBacklogItemId);
        this.setCategory(aCategory);
        // 从可用的待办清单里，获取未请求过的讨论；设置好
        this.setDiscussion(
                BacklogItemDiscussion
                        .fromAvailability(DiscussionAvailability.NOT_REQUESTED));
        this.setProductId(aProductId);
        this.setStatus(aStatus);
        this.setStoryPoints(aStoryPoints);
        this.setSummary(aSummary);
        this.setTenantId(aTenantId);
        this.setType(aType);
    }

    /**
     * 获取所有的任务清单
     * @return 返回所有的任务清单
     */
    public Set<Task> allTasks() {
        return Collections.unmodifiableSet(this.tasks());
    }

    /**
     * 是否有剩余工作时间
     * @return
     */
    public boolean anyTaskHoursRemaining() {
        return this.totalTaskHoursRemaining() > 0;
    }

    /**
     * 获取相关问题编号
     * @return 返回相关问题编号
     */
    public String associatedIssueId() {
        return this.associatedIssueId;
    }

    /**
     * 使用问题编号关联问题
     * @param anIssueId 问题编号
     */
    public void associateWithIssue(String anIssueId) {
        if (this.associatedIssueId == null) {
            this.associatedIssueId = anIssueId;
        }
    }

    /**
     * 分配业务优先级
     * @param aBusinessPriority 业务优先级
     */
    public void assignBusinessPriority(BusinessPriority aBusinessPriority) {
        // 首先，设置业务优先级
        this.setBusinessPriority(aBusinessPriority);

        // 然后，领域事件发布者发布 业务优先级已分配
        DomainEventPublisher
                .instance()
                .publish(new BusinessPriorityAssigned(
                        this.tenantId(),
                        this.backlogItemId(),
                        this.businessPriority()));
    }

    /**
     * 分配故事点清单
     * @param aStoryPoints
     */
    public void assignStoryPoints(StoryPoints aStoryPoints) {
        // 首先，设置故事点清单
        this.setStoryPoints(aStoryPoints);

        // 然后，领域事件发布者发布：待办故事点清单已分配
        DomainEventPublisher
                .instance()
                .publish(new BacklogItemStoryPointsAssigned(
                        this.tenantId(),
                        this.backlogItemId(),
                        this.storyPoints()));
    }

    /**
     * 分配任务志愿者
     * @param aTaskId       任务ID
     * @param aVolunteer    志愿者（团队成员）
     */
    public void assignTaskVolunteer(TaskId aTaskId, TeamMember aVolunteer) {

        // 根据任务ID，从任务清单中找到对应的任务
        Task task = this.task(aTaskId);

        // 如果任务无效，就抛出异常
        if (task == null) {
            throw new IllegalStateException("Task does not exist.");
        }


        // 把团队成员分配给任务
        task.assignVolunteer(aVolunteer);
    }

    /**
     * 获取待办事项ID
     * @return 返回待办事项ID
     */
    public BacklogItemId backlogItemId() {
        return this.backlogItemId;
    }

    /**
     * 获取业务优先级
     * @return 返回业务优先级
     */
    public BusinessPriority businessPriority() {
        return this.businessPriority;
    }

    /**
     * 获取范围
     * @return 返回范围
     */
    public String category() {
        return this.category;
    }

    /**
     * 改变范围
     * @param aCategory 范围
     */
    public void changeCategory(String aCategory) {
        // 首先，设置新范围
        this.setCategory(aCategory);

        // 然后，领域事件发布者发布：待办事项范围已改变
        DomainEventPublisher
                .instance()
                .publish(new BacklogItemCategoryChanged(
                        this.tenantId(),
                        this.backlogItemId(),
                        this.category()));
    }

    /**
     * 改变任务状态
     * @param aTaskId   任务ID
     * @param aStatus   任务状态
     */
    public void changeTaskStatus(TaskId aTaskId, TaskStatus aStatus) {
        // 根据任务ID，从任务清单中找到对应的任务
        Task task = this.task(aTaskId);

        // 如果任务无效，则抛出异常
        if (task == null) {
            throw new IllegalStateException("Task does not exist.");
        }
        // 改变任务状态
        task.changeStatus(aStatus);
    }

    /**
     * 改变类型
     * @param aType 类型
     */
    public void changeType(BacklogItemType aType) {
        // 首先，设置待办事项类型
        this.setType(aType);

        // 然后，领域事件发布者发布：待办事项类型已改变
        DomainEventPublisher
                .instance()
                .publish(new BacklogItemTypeChanged(
                        this.tenantId(),
                        this.backlogItemId(),
                        this.type()));
    }

    /**
     * 承诺冲刺（目标）
     * @param aSprint 冲刺（目标）
     */
    public void commitTo(Sprint aSprint) {
        this.assertArgumentNotNull(aSprint, "Sprint must not be null.");
        this.assertArgumentEquals(aSprint.tenantId(), this.tenantId(), "Sprint must be of same tenant.");
        this.assertArgumentEquals(aSprint.productId(), this.productId(), "Sprint must be of same product.");

        // 检查是否已经安排好发布
        if (!this.isScheduledForRelease()) {
            throw new IllegalStateException("Must be scheduled for release to commit to sprint.");
        }
        // 检查是否已承诺冲刺（目标）
        if (this.isCommittedToSprint()) {
            if (!aSprint.sprintId().equals(this.sprintId())) {
                this.uncommitFromSprint();
            }
        }

        // 把状态提升至”已承诺“
        this.elevateStatusWith(BacklogItemStatus.COMMITTED);

        // 使用冲刺(目标)的ID设置
        this.setSprintId(aSprint.sprintId());

        // 领域事件发布者发布：待办事项已承诺
        DomainEventPublisher
                .instance()
                .publish(new BacklogItemCommitted(
                        this.tenantId(),
                        this.backlogItemId(),
                        this.sprintId()));
    }

    /**
     * 定义一个新任务
     * @param aVolunteer        团队成员
     * @param aName             任务名
     * @param aDescription      内容描述
     * @param anHoursRemaining  剩余小时数
     */
    public void defineTask(TeamMember aVolunteer, String aName, String aDescription, int anHoursRemaining) {

        // 追加一个新任务
        Task task = new Task(
                this.tenantId(),
                this.backlogItemId(),
                new TaskId(),
                aVolunteer,
                aName,
                aDescription,
                anHoursRemaining,
                TaskStatus.NOT_STARTED);

        this.tasks().add(task);

        // 领域事件发布者发布：任务已定义
        DomainEventPublisher
                .instance()
                .publish(new TaskDefined(
                        this.tenantId(),
                        this.backlogItemId(),
                        task.taskId(),
                        aVolunteer.username(),
                        aName,
                        aDescription,
                        anHoursRemaining));
    }

    /**
     * 描述任务
     * @param aTaskId       任务ID
     * @param aDescription  内容描述
     */
    public void describeTask(TaskId aTaskId, String aDescription) {
        // 根据任务ID，从任务清单中找到对应的任务
        Task task = this.task(aTaskId);
        // 如果任务无效，则抛出异常
        if (task == null) {
            throw new IllegalStateException("Task does not exist.");
        }
        // 设置内容描述
        task.describeAs(aDescription);
    }

    /**
     * 获取待办事项讨论
     * @return 返回待办事项讨论
     */
    public BacklogItemDiscussion discussion() {
        return this.discussion;
    }

    /**
     * 获取讨论发起ID
     * @return  返回讨论发起ID
     */
    public String discussionInitiationId() {
        return this.discussionInitiationId;
    }

    /**
     * 失败讨论发起
     */
    public void failDiscussionInitiation() {
        if (!this.discussion().availability().isReady()) {
            this.setDiscussionInitiationId(null);
            this.setDiscussion(
                    BacklogItemDiscussion
                            .fromAvailability(DiscussionAvailability.FAILED));
        }
    }

    /**
     * 初始化讨论
     * @param aDescriptor
     */
    public void initiateDiscussion(DiscussionDescriptor aDescriptor) {
        if (aDescriptor == null) {
            throw new IllegalArgumentException("The descriptor must not be null.");
        }

        // 如果讨论可用且已经请求的情况下，发布领域事件
        if (this.discussion().availability().isRequested()) {
            // 给准备好的讨论设置讨论描述器
            this.setDiscussion(this.discussion().nowReady(aDescriptor));

            // 领域事件发布者发布：待办事项讨论已发起
            DomainEventPublisher
                    .instance()
                    .publish(new BacklogItemDiscussionInitiated(
                            this.tenantId(),
                            this.backlogItemId(),
                            this.discussion()));
        }
    }

    /**
     * 估计剩余的工作时间
     * @param aTaskId           任务ID
     * @param anHoursRemaining  剩余时间
     */
    public void estimateTaskHoursRemaining(TaskId aTaskId, int anHoursRemaining) {
        // 根据任务ID，从任务清单中找到对应的任务
        Task task = this.task(aTaskId);

        if (task == null) {
            throw new IllegalStateException("Task does not exist.");
        }

        // 给任务设置：估计剩余工作时间
        task.estimateHoursRemaining(anHoursRemaining);

        BacklogItemStatus changedStatus = null;

        // 如果没有剩余时间且总剩余时间也没有的情况下，把状态设置为已完成
        if (anHoursRemaining == 0) {
            if (!this.anyTaskHoursRemaining()) {
                changedStatus = BacklogItemStatus.DONE;
            }
        } else if (this.isDone()) {// 如果有剩余时间
            // regress to the logical previous state
            // because "done" is no longer appropriate
            if (this.isCommittedToSprint()) {
                changedStatus = BacklogItemStatus.COMMITTED;//已承诺
            } else if (this.isScheduledForRelease()) {
                changedStatus = BacklogItemStatus.SCHEDULED;//已预定
            } else {
                changedStatus = BacklogItemStatus.PLANNED;//已计划
            }
        }

        if (changedStatus != null) {
            // 把状态设置成新状态
            this.setStatus(changedStatus);

            // 领域事件发布者发布：待办事项状态已改变
            DomainEventPublisher
                    .instance()
                    .publish(new BacklogItemStatusChanged(
                            this.tenantId(),
                            this.backlogItemId(),
                            changedStatus));
        }
    }

    /**
     * 是否有业务优先级
     * @return 返回是否有业务优先级
     */
    public boolean hasBusinessPriority() {
        return this.businessPriority() != null;
    }

    /**
     * 发起讨论
     * @param aDiscussion   待定项目讨论
     */
    public void initiateDiscussion(BacklogItemDiscussion aDiscussion) {
        // 首先，设置待定项目讨论
        this.setDiscussion(aDiscussion);

        // 然后，领域事件发布者发布：待定项目讨论已发起
        DomainEventPublisher
                .instance()
                .publish(new BacklogItemDiscussionInitiated(
                        this.tenantId(),
                        this.backlogItemId(),
                        this.discussion()));
    }

    /**
     * 检查是否已承诺冲刺目标
     * @return 返回承诺与否
     */
    public boolean isCommittedToSprint() {
        return this.sprintId() != null;
    }

    /**
     * 是否已完成
     * @return
     */
    public boolean isDone() {
        return this.status().isDone();
    }

    /**
     * 是否已计划
     * @return
     */
    public boolean isPlanned() {
        return this.status().isPlanned();
    }

    /**
     * 是否已移除
     * @return
     */
    public boolean isRemoved() {
        return this.status().isRemoved();
    }

    /**
     * 是否已预定发布
     * @return
     */
    public boolean isScheduledForRelease() {
        return this.releaseId() != null;
    }

    /**
     * 标记为已删除
     */
    public void markAsRemoved() {
        // 如果已删除，抛出异常
        if (this.isRemoved()) {
            throw new IllegalStateException("Already removed, not outstanding.");
        }
        // 如果已完成，抛出异常
        if (this.isDone()) {
            throw new IllegalStateException("Already done, not outstanding.");
        }
        // 如果已承诺冲刺（目标）
        if (this.isCommittedToSprint()) {
            this.uncommitFromSprint();
        }
        if (this.isScheduledForRelease()) {
            this.unscheduleFromRelease();
        }

        this.setStatus(BacklogItemStatus.REMOVED);

        DomainEventPublisher
                .instance()
                .publish(new BacklogItemMarkedAsRemoved(
                        this.tenantId(),
                        this.backlogItemId()));
    }

    public ProductId productId() {
        return this.productId;
    }

    public ReleaseId releaseId() {
        return this.releaseId;
    }

    public void removeTask(TaskId aTaskId) {
        Task task = this.task(aTaskId);

        if (task == null) {
            throw new IllegalStateException("Task does not exist.");
        }

        if (!this.tasks().remove(task)) {
            throw new IllegalStateException("Task was not removed.");
        }

        DomainEventPublisher
                .instance()
                .publish(new TaskRemoved(
                        this.tenantId(),
                        this.backlogItemId(),
                        aTaskId));
    }

    public void renameTask(TaskId aTaskId, String aName) {
        Task task = this.task(aTaskId);

        if (task == null) {
            throw new IllegalStateException("Task does not exist.");
        }

        task.rename(aName);
    }

    public void requestDiscussion(DiscussionAvailability aDiscussionAvailability) {
        if (!this.discussion().availability().isReady()) {
            this.setDiscussion(
                    BacklogItemDiscussion.fromAvailability(
                            aDiscussionAvailability));

            DomainEventPublisher
                    .instance()
                    .publish(new BacklogItemDiscussionRequested(
                            this.tenantId(),
                            this.productId(),
                            this.backlogItemId(),
                            this.discussion().availability().isRequested()));
        }
    }

    public void scheduleFor(Release aRelease) {
        this.assertArgumentNotNull(aRelease, "Release must not be null.");
        this.assertArgumentEquals(aRelease.tenantId(), this.tenantId(), "Release must be of same tenant.");
        this.assertArgumentEquals(aRelease.productId(), this.productId(), "Release must be of same product.");

        if (this.isScheduledForRelease()) {
            if (!aRelease.releaseId().equals(this.releaseId())) {
                this.unscheduleFromRelease();
            }
        }

        if (this.status().isPlanned()) {
            this.setStatus(BacklogItemStatus.SCHEDULED);
        }

        this.setReleaseId(aRelease.releaseId());

        DomainEventPublisher
                .instance()
                .publish(new BacklogItemScheduled(
                        this.tenantId(),
                        this.backlogItemId(),
                        this.releaseId()));
    }

    public SprintId sprintId() {
        return this.sprintId;
    }

    public void startDiscussionInitiation(String aDiscussionInitiationId) {
        if (!this.discussion().availability().isReady()) {
            this.setDiscussionInitiationId(aDiscussionInitiationId);
        }
    }

    public String story() {
        return this.story;
    }

    public StoryPoints storyPoints() {
        return this.storyPoints;
    }

    public String summary() {
        return this.summary;
    }

    public void summarize(String aSummary) {
        this.setSummary(aSummary);

        DomainEventPublisher
                .instance()
                .publish(new BacklogItemSummarized(
                        this.tenantId(),
                        this.backlogItemId(),
                        this.summary()));
    }

    public Task task(TaskId aTaskId) {

        for (Task task : this.tasks()) {
            if (task.taskId().equals(aTaskId)) {
                return task;
            }
        }

        return null;
    }

    public void tellStory(String aStory) {
        this.setStory(aStory);

        DomainEventPublisher
                .instance()
                .publish(new BacklogItemStoryTold(
                        this.tenantId(),
                        this.backlogItemId(),
                        this.story()));
    }

    public TenantId tenantId() {
        return this.tenantId;
    }

    public int totalTaskHoursRemaining() {
        int totalHoursRemaining = 0;

        for (Task task : this.tasks()) {
            totalHoursRemaining += task.hoursRemaining();
        }

        return totalHoursRemaining;
    }

    public BacklogItemType type() {
        return this.type;
    }

    /**
     * 从冲刺取消提交
     */
    public void uncommitFromSprint() {
        if (!this.isCommittedToSprint()) {
            throw new IllegalStateException("Not currently committed.");
        }

        this.setStatus(BacklogItemStatus.SCHEDULED);
        SprintId uncommittedSprintId = this.sprintId();
        this.setSprintId(null);

        DomainEventPublisher
                .instance()
                .publish(new BacklogItemUncommitted(
                        this.tenantId(),
                        this.backlogItemId(),
                        uncommittedSprintId));
    }

    /**
     * 从发布取消预订
     */
    public void unscheduleFromRelease() {
        // 如果已提交到冲刺，则抛出异常
        if (this.isCommittedToSprint()) {
            throw new IllegalStateException("Must first uncommit.");
        }
        // 如果为发布以预订，则抛出异常
        if (!this.isScheduledForRelease()) {
            throw new IllegalStateException("Not scheduled for release.");
        }

        // 设置状态为：已计划
        this.setStatus(BacklogItemStatus.PLANNED);
        // 获取已发布ID为未预订
        ReleaseId unscheduledReleaseId = this.releaseId();
        // 然后 设置发布ID为无效
        this.setReleaseId(null);

        // 领域事件发布者发布：未预订状态
        DomainEventPublisher
                .instance()
                .publish(new BacklogItemUnscheduled(
                        this.tenantId(),
                        this.backlogItemId(),
                        unscheduledReleaseId));
    }

    @Override
    public boolean equals(Object anObject) {
        boolean equalObjects = false;

        if (anObject != null && this.getClass() == anObject.getClass()) {
            BacklogItem typedObject = (BacklogItem) anObject;
            equalObjects =
                    this.tenantId().equals(typedObject.tenantId()) &&
                            this.productId().equals(typedObject.productId()) &&
                            this.backlogItemId().equals(typedObject.backlogItemId());
        }

        return equalObjects;
    }

    @Override
    public int hashCode() {
        int hashCodeValue =
                + (34685 * 7)
                        + this.tenantId().hashCode()
                        + this.productId().hashCode()
                        + this.backlogItemId().hashCode();

        return hashCodeValue;
    }

    @Override
    public String toString() {
        return "BacklogItem [tenantId=" + tenantId + ", productId=" + productId
                + ", backlogItemId=" + backlogItemId
                + ", businessPriority=" + businessPriority
                + ", category=" + category + ", discussion=" + discussion
                + ", releaseId=" + releaseId + ", sprintId=" + sprintId
                + ", status=" + status + ", story=" + story
                + ", storyPoints=" + storyPoints + ", summary=" + summary
                + ", tasks=" + tasks + ", type=" + type + "]";
    }

    /**
     * 待办事项默认构造器
     */
    private BacklogItem() {
        // 调用 父类默认构造器
        super();
        // 初始化任务清单
        this.setTasks(new HashSet<Task>(0));
    }

    /**
     * 设置 待办事项ID
     * @param aBacklogItemId    待办事项ID
     */
    private void setBacklogItemId(BacklogItemId aBacklogItemId) {
        this.assertArgumentNotNull(aBacklogItemId, "The backlogItemId must be provided.");

        this.backlogItemId = aBacklogItemId;
    }

    /**
     * 设置业务优先级
     * @param aBusinessPriority 业务优先级
     */
    private void setBusinessPriority(BusinessPriority aBusinessPriority) {
        this.businessPriority = aBusinessPriority;
    }

    /**
     * 设置范围
     * @param aCategory 范围
     */
    private void setCategory(String aCategory) {
        this.assertArgumentNotEmpty(aCategory, "The category must be provided.");
        this.assertArgumentLength(aCategory, 25, "The category must be 25 characters or less.");

        this.category = aCategory;
    }

    /**
     * 设置待办事项讨论
     * @param aDiscussion   待办事项讨论
     */
    private void setDiscussion(BacklogItemDiscussion aDiscussion) {
        this.discussion = aDiscussion;
    }

    /**
     * 设置讨论发起ID
     * @param aDiscussionInitiationId   讨论发起ID
     */
    private void setDiscussionInitiationId(String aDiscussionInitiationId) {
        if (aDiscussionInitiationId != null) {
            this.assertArgumentLength(
                    aDiscussionInitiationId,
                    100,
                    "Discussion initiation identity must be 100 characters or less.");
        }

        this.discussionInitiationId = aDiscussionInitiationId;
    }

    /**
     * 设置 产品ID
     * @param aProductId    产品ID
     */
    private void setProductId(ProductId aProductId) {
        this.assertArgumentNotNull(aProductId, "The product id must be provided.");

        this.productId = aProductId;
    }

    /**
     * 设置发布ID
     * @param aReleaseId 发布ID
     */
    private void setReleaseId(ReleaseId aReleaseId) {
        this.releaseId = aReleaseId;
    }

    /**
     * 设置冲刺ID
     * @param aSprintId 冲刺ID
     */
    private void setSprintId(SprintId aSprintId) {
        this.sprintId = aSprintId;
    }

    /**
     * 获取 待办事项状态
     * @return  返回待办事项状态
     */
    private BacklogItemStatus status() {
        return this.status;
    }

    /**
     * 提升状态
     * @param aStatus 待办事项状态
     */
    private void elevateStatusWith(BacklogItemStatus aStatus) {
        // 如果 待办事项状态 已预定的话，把状态提升为已提交
        if (this.status().isScheduled()) {
            this.setStatus(BacklogItemStatus.COMMITTED);
        }
    }

    /**
     * 设置 待办事项状态
     * @param aStatus 待办事项状态
     */
    private void setStatus(BacklogItemStatus aStatus) {
        this.status = aStatus;
    }

    /**
     * 设置故事
     * @param aStory    故事
     */
    private void setStory(String aStory) {
        if (aStory != null) {
            this.assertArgumentLength(aStory, 65000, "The story must be 65000 characters or less.");
        }

        this.story = aStory;
    }

    /**
     * 设置故事点数
     * @param aStoryPoints  故事点数
     */
    private void setStoryPoints(StoryPoints aStoryPoints) {
        this.storyPoints = aStoryPoints;
    }

    /**
     * 设置摘要
     * @param aSummary  摘要
     */
    private void setSummary(String aSummary) {
        this.assertArgumentNotEmpty(aSummary, "The summary must be provided.");
        this.assertArgumentLength(aSummary, 100, "The summary must be 100 characters or less.");

        this.summary = aSummary;
    }

    /**
     * 获取 任务清单
     * @return  返回任务清单
     */
    private Set<Task> tasks() {
        return this.tasks;
    }

    /**
     * 设置 任务清单
     * @param aTasks    任务清单
     */
    private void setTasks(Set<Task> aTasks) {
        this.tasks = aTasks;
    }

    /**
     * 设置租户ID
     * @param aTenantId 租户ID
     */
    private void setTenantId(TenantId aTenantId) {
        this.assertArgumentNotNull(aTenantId, "The tenant id must be provided.");

        this.tenantId = aTenantId;
    }

    /**
     * 设置待办事项类型
     * @param aType 待办事项类型
     */
    private void setType(BacklogItemType aType) {
        this.assertArgumentNotNull(aType, "The backlog item type must be provided.");

        this.type = aType;
    }
}
